package com.cicdi.core.util;

import com.alaya.contracts.ppos.DelegateContract;
import com.alaya.contracts.ppos.abi.Function;
import com.alaya.contracts.ppos.dto.CallResponse;
import com.alaya.contracts.ppos.dto.resp.Delegation;
import com.alaya.contracts.ppos.dto.resp.DelegationIdInfo;
import com.alaya.contracts.ppos.utils.EncoderUtils;
import com.alaya.crypto.Credentials;
import com.alaya.parameters.NetworkParameters;
import com.alaya.protocol.Web3j;
import com.alaya.protocol.core.RemoteCall;
import com.alaya.protocol.core.methods.response.TransactionReceipt;
import com.alaya.utils.Convert;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author haypo
 * @date 2020/12/2
 */
@Slf4j
public class UndelegateUtil {
    private final SendUtil sendUtil;
    private final Web3j web3j;

    public UndelegateUtil(String url) {
        sendUtil = new SendUtil(url);
        web3j = Web3j.build(sendUtil.web3jService);
    }

    /**
     * @param nodeId          节点id
     * @param credentials     钱包
     * @param atpAmount       赎回量，单位为atp
     * @param stakingBlockNum 质押区块号
     * @param nonce           nonce值
     */
    public void fastUnDelegateWithMidGas(String nodeId, Credentials credentials, BigDecimal atpAmount, BigInteger stakingBlockNum, long nonce) {
        if (atpAmount.doubleValue() < Common.minDelegate) {
            return;
        }
        BigDecimal vonAmount = Convert.toVon(MathUtil.round6(atpAmount), Convert.Unit.ATP);
        try {
            Function function = FunctionUtil.createUnDelegateFunction(nodeId, stakingBlockNum, vonAmount.toBigIntegerExact());
            String data = EncoderUtils.functionEncoder(function);
            String contractAddressOfStaking = NetworkParameters.getPposContractAddressOfStaking(NetworkParameters.CurrentNetwork.getChainId());
            sendUtil.fastSend(credentials, nonce, BigDecimal.ZERO, contractAddressOfStaking, data, Common.MID_GAS_PROVIDER);
            log.info("地址：{} 从{} 赎回委托，金额{}",
                    credentials.getAddress(), nodeId, MathUtil.round6(atpAmount));
        } catch (Exception e) {
            log.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 根据钱包获得委托id信息
     *
     * @param credentials 钱包
     * @return 委托id信息列表
     */
    public List<DelegationIdInfo> getDelegationIdInfoList(Credentials credentials) {
        try {
            DelegateContract delegateContract = DelegateContract.load(web3j, credentials, NetworkParameters.CurrentNetwork.getChainId());
            RemoteCall<CallResponse<List<DelegationIdInfo>>> remoteCall = delegateContract.getRelatedListByDelAddr(credentials.getAddress());
            return remoteCall.send().getData();
        } catch (Exception e) {
            log.info(e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    /**
     * 根据钱包获得委托信息列表
     *
     * @param credentials 钱包
     * @return 委托信息列表
     */
    public List<Delegation> getDelegations(Credentials credentials) {
        List<DelegationIdInfo> delegationIdInfoList = getDelegationIdInfoList(credentials);
        return delegationIdInfoList.stream().map(
                dif -> getDelegation(credentials, dif)
        ).collect(Collectors.toList());
    }


    /**
     * 获得委托信息
     *
     * @param credentials      钱包
     * @param delegationIdInfo 委托id信息
     * @return 委托信息
     */
    public Delegation getDelegation(Credentials credentials, DelegationIdInfo delegationIdInfo) {
        try {
            DelegateContract delegateContract = DelegateContract.load(Web3j.build(sendUtil.web3jService),
                    credentials,
                    NetworkParameters.CurrentNetwork.getChainId());
            RemoteCall<CallResponse<Delegation>> remoteCall = delegateContract.getDelegateInfo(delegationIdInfo.getNodeId(), credentials.getAddress(), delegationIdInfo.getStakingBlockNum());
            return remoteCall.send().getData();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据钱包赎回全部委托，速度快，建议用于快速赎回钱包全部委托
     *
     * @param credentials 钱包
     * @throws Exception io流异常
     */
    public void fastUndelegateAll(Credentials credentials) throws Exception {
        List<Delegation> delegations = getDelegations(credentials);
        long nonce = NonceUtil.getNonce(web3j, credentials).longValue();
        for (Delegation delegation : delegations) {
            BigDecimal atpAmount = Convert.fromVon(new BigDecimal(delegation.getDelegateReleased()), Convert.Unit.ATP);
            fastUnDelegateWithMidGas(delegation.getNodeId(), credentials, atpAmount, delegation.getStakingBlockNum(), nonce++);
        }
    }


    /**
     * 根据钱包赎回指定的全部委托，速度较慢，适合单个赎回
     *
     * @param credentials 钱包
     * @param delegation  委托信息
     * @throws Exception io流异常
     */
    public void undelegateAll(Credentials credentials, Delegation delegation) throws Exception {
        DelegateContract delegateContract = DelegateContract.load(web3j, credentials,
                NetworkParameters.CurrentNetwork.getChainId());
        BigInteger delegateReleased = delegation.getDelegateReleased();

        TransactionReceipt receipt = delegateContract.unDelegate(
                delegation.getNodeId(), delegation.getStakingBlockNum(),
                delegateReleased, Common.MID_GAS_PROVIDER)
                .send().getTransactionReceipt();
        log.info(receipt.toString());
    }
}
